/* -----------------
* brief
* 1. this is code gen by tools
 */

package service

import (
	"errors"
	"fmt"
	wxcard "github.com/chanxuehong/wechat/mp/card"
	"github.com/chanxuehong/wechat/mp/core"
	"github.com/mjiulee/lego"
	"saas_sys_base/common"
	. "saas_sys_base/modules/merchantCard/models"
)

type MchUserMemberCardService struct {
	// add your model here
	tbMchUserMemberCard MchUserMemberCard
	tbMchMemberCard MchMemberCard
	tbMemberCardAccountLog MemberCardAccountLog

	mchUserMemberCardDal MchUserMemberCardDal
	memberCardAccountLogService MemberCardAccountLogService
}

func (t *MchUserMemberCardService) ListMchUserMemberCard(keyword string, page, psize int, orderField, order string) ([]map[string]string, int64, error) {
	// 业务查询
	queryMap := map[string]interface{}{
		"table":  map[string]string{"table": "tb_mch_user_member_card", "alias": "t1"},
		"fields": []string{"t1.*"},
		"join":   []map[string]string{
			// map[string]string{"table": "tb_xx as t2", "on": "t2.xid = t1.id"},
		},
		"where": []lego.QMapWhereField{
			{"t1.if_del", "=", 0, "AND", false},
		},
		"search": map[string]string{
			//"t1.id": req.Keyword,
		},
		"orders": []string{"t1.id asc"},
		"page": map[string]int{
			"page":  page,
			"psize": psize,
		},
	}

	if orderField != "" && order != "" {
		orderStr := fmt.Sprintf("t1.%s %s", orderField, order)
		queryMap["orders"] = []string{orderStr, "t1.cdate asc"}
	}

	return lego.QueryListByMap(queryMap)
}

func (t *MchUserMemberCardService) ListMchUserMemberCardByWho(who *common.Who, keyword string, page, psize int, orderField, order string) ([]map[string]string, int64, error) {
	// 业务查询
	queryMap := map[string]interface{}{
		"table":  map[string]string{"table": "tb_mch_user_member_card", "alias": "t1"},
		"fields": []string{"t1.*"},
		"join":   []map[string]string{
			//map[string]string{"table": "tb_xxx as t2", "on": "t2.xxx = t1.xxx"},
		},
		"where": []lego.QMapWhereField{
			{"t1.if_del", "=", 0, "AND", false},
			//{"xx.tenant_id", "=", who.TenantId, "AND", false},
			//{"xx.mini_app_id", "=", who.MiniAppId, "AND", false},

		},
		"search": map[string]string{
			//"t1.id": req.Keyword,
		},
		"orders": []string{"t1.id asc"},
		"page": map[string]int{
			"page":  page,
			"psize": psize,
		},
	}

	if orderField != "" && order != "" {
		orderStr := fmt.Sprintf("t1.%s %s", orderField, order)
		queryMap["orders"] = []string{orderStr, "t1.cdate asc"}
	}
	/*{ //这段生成代码的目的是，当需要根据条件判断，来增加对应where条件时，使用这里的逻辑
		queryWhere := queryMap["where"].([]lego.QMapWhereField)
		if who.TenantId != 0 {
			queryWhere = append(queryWhere, lego.QMapWhereField{"t2.tenant_id", "=", who.TenantId, "AND", false})
		}

		if who.MiniAppId != 0 {
			queryWhere = append(queryWhere, lego.QMapWhereField{"t2.mini_app_id", "=", who.MiniAppId, "AND", false})
		}

	}*/

	return lego.QueryListByMap(queryMap)
}

/* 根据id获取记录
 * -----------------
 */
func (t *MchUserMemberCardService) GetMchUserMemberCardById(id int64) *MchUserMemberCard {
	return t.tbMchUserMemberCard.RecodeById(id)
}

/* 添加
 * -----------------
 */
func (t *MchUserMemberCardService) SaveMchUserMemberCard(item2add *MchUserMemberCard) bool {
	return t.tbMchUserMemberCard.AddRecode(item2add)
}

/* 删除(硬删除)*/
func (t *MchUserMemberCardService) DelMchUserMemberCardById(id int64) bool {
	return t.tbMchUserMemberCard.SDelRecodeById(id)
}

/* 更新
 * -----------------*/
func (t *MchUserMemberCardService) UpdateMchUserMemberCard(rc *MchUserMemberCard) error {
	_, err := t.tbMchUserMemberCard.UpdateRecode(rc)
	return err
}

//处理来自微信推送的领卡消息

func (t *MchUserMemberCardService) CardGetCardEventHandler(userWho *common.Who,
														weappid string,
														mixedMsg *core.MixedMsg) (*MchUserMemberCard, error) {
	msg := wxcard.GetUserGetCardEvent(mixedMsg)
	//这里取出来，是为了WxCardId转成MchCardId
	mchMemberCard := t.tbMchMemberCard.RecodeByWxCardId(msg.CardId)
	if mchMemberCard == nil {
		return nil, fmt.Errorf("not found cardid	<%s> ", msg.CardId)
	}

	mchUserMemberCard, err := t.CardGetCardEventHandlerForMchCard(userWho, weappid, mchMemberCard.Id, mixedMsg, nil)
	return mchUserMemberCard, err
}


/*
mixedMsg为非nil，则为处理微信侧推送来的消息；
mixedMsg为nil，则为处理商户卡的消息；
*/
func (t *MchUserMemberCardService) CardGetCardEventHandlerForMchCard(userWho *common.Who,
	                                                       weappid string,
	                                                       mchCardId int64, mixedMsg *core.MixedMsg, memberInfo *MemberInfo) (*MchUserMemberCard, error) {
	msg := wxcard.GetUserGetCardEvent(mixedMsg)

	mchMemberCard := t.tbMchMemberCard.RecodeById(mchCardId)
	if mchMemberCard == nil {
		return nil, fmt.Errorf("not found cardid	<%s> ", msg.CardId)
	}

	if !userWho.IsEqualTenant(&mchMemberCard.Who) {
		return nil, fmt.Errorf("user in other tenant can not fetch this card<%s> ", msg.CardId)
	}

	count, err := t.tbMchUserMemberCard.CountByMchCardIdAndUid(mchMemberCard.Id, userWho.Uid)
	if err != nil {
		return nil, errors.New("MchUserMemberCardService.CountByMchCardIdAndUid failed")
	}

	if count >= int64(mchMemberCard.MemberCard.BaseInfo.GetLimit) {
		return nil, errors.New("已领取过")
	}

	if mchMemberCard.CardMode == CARD_MODE_MCH_WECHAT ||
		mchMemberCard.CardMode == CARD_MODE_WECHAT {
		//微信推送场景时，防止重复处理同一UserCardCode
		//除了在这里做防护，weappid、cardid、userCardCode的数据库字段也配置了unique组
		usercard := t.tbMchUserMemberCard.ReocodeByWeappIdCardIdAndCardCode(weappid, msg.CardId, msg.UserCardCode)
		if usercard != nil {
			return usercard, nil
		}
	}

	item2add := NewMchUserMemberCard(userWho, weappid, mchMemberCard, msg, memberInfo)
	isSuccess := t.tbMchUserMemberCard.AddRecode(item2add)
	if !isSuccess {
		return nil, errors.New("tbMchCardUserCard.AddRecodeWithsession failed")
	}

	return item2add, nil
}

func (t *MchUserMemberCardService) UserDelCardEventHandler(weappid string, mixedMsg *core.MixedMsg) error {
	msg := wxcard.GetUserDelCardEvent(mixedMsg)
	//weappid := msg.AppId
	cardid := msg.CardId
	cardcode := msg.UserCardCode

	isSuccess := t.tbMchUserMemberCard.SDelRecodeByWeappIdCardIdAndCardCode(weappid, cardid, cardcode)
	if !isSuccess {
		return errors.New("tbMchUserMemberCard.SDelRecodeByWeappIdCardIdAndCardCode failed")
	}

	return nil
}

func (t *MchUserMemberCardService) UserConsumeCardEventHandler(weappid string, mixedMsg *core.MixedMsg) error {
	msg := wxcard.GetUserConsumeCardEvent(mixedMsg)
	//weappid := msg.AppId
	cardid := msg.CardId
	cardcode := msg.UserCardCode

	userMemberCard:= new(MchUserMemberCard)
	userMemberCard.SetConsume(msg)
	isSuccess := t.tbMchUserMemberCard.UpdateRecodeByWeappIdCardIdAndCardCode(userMemberCard, weappid, cardid, cardcode)
	if !isSuccess {
		return errors.New("tbMchUserMemberCard.UpdateRecodeByWeappIdCardIdAndCardCode failed")
	}

	return nil
}

func (t *MchUserMemberCardService) UserViewCardEventHandler(weappid string, mixedMsg *core.MixedMsg) error {
	// TODO统计计数以后再考虑
	return nil
}

func (t *MchUserMemberCardService) UserEnterSessionEventHandler(weappid string, mixedMsg *core.MixedMsg) error {
	// TODO以后再考虑
	return nil
}

func (t *MchUserMemberCardService) SubmitMemberCardUserInfoEventHandler(weappid string, mixedMsg *core.MixedMsg) error {
	return nil
}


func (t *MchUserMemberCardService) GetUserGetCardEvent(msg *core.MixedMsg) *wxcard.UserGetCardEvent {
	return &wxcard.UserGetCardEvent{
		MsgHeader:           msg.MsgHeader,
		EventType:           msg.EventType,
		CardId:              msg.CardId,
		IsGiveByFriend:      msg.IsGiveByFriend,
		FriendUserName:      msg.FriendUserName,
		UserCardCode:        msg.UserCardCode,
		OldUserCardCode:     msg.OldUserCardCode,
		OuterId:             msg.OuterId,
		OuterStr:            msg.OuterStr,
		IsRestoreMemberCard: msg.IsRestoreMemberCard,
		IsRecommendByFriend: msg.IsRecommendByFriend,
	}
}



func (t *MchUserMemberCardService) AddBalance(userWho *common.Who, userCardId int64, balanceAddend CardAddBalance) error {


	s := lego.NewDBSession()
	s.Begin()
	defer s.Close()

	err := t.mchUserMemberCardDal.AddBalance(s, userCardId, balanceAddend)
	if err != nil {
		s.RollbackWithLogErr(err)
		return err
	}

	if balanceAddend.AddBalance != 0 {
		item2add := new(MemberCardAccountLog)

		item2add.AddBalance = balanceAddend.AddBalance

		item2add.UserCardId = userCardId
		item2add.Who = *userWho
		item2add.IncomeType = balanceAddend.IncomeType
		item2add.BusinessType = balanceAddend.BusinessType
		item2add.PayChan = balanceAddend.PayChan
		item2add.Description = balanceAddend.Description
		isSuccess := t.tbMemberCardAccountLog.AddRecodeWithsession(s.GetSession(), item2add)
		if isSuccess != true {
			err := errors.New("tbMemberCardAccountLog.AddRecodeWithsession")
			s.RollbackWithLogErr(err)
			return err
		}
	}

	if balanceAddend.GiveBalance != 0 {
		item2add := new(MemberCardAccountLog)

		item2add.AddBalance = balanceAddend.GiveBalance

		item2add.UserCardId = userCardId
		item2add.Who = *userWho
		item2add.IncomeType = balanceAddend.IncomeType
		item2add.BusinessType = BusinessType_Give
		item2add.PayChan = balanceAddend.PayChan
		item2add.Description = balanceAddend.Description
		isSuccess := t.tbMemberCardAccountLog.AddRecodeWithsession(s.GetSession(), item2add)
		if isSuccess != true {
			err := errors.New("tbMemberCardAccountLog.AddRecodeWithsession")
			s.RollbackWithLogErr(err)
			return err
		}
	}

	if balanceAddend.AddBonus != 0 {
		item2add := new(MemberCardAccountLog)

		item2add.AddBonus = balanceAddend.AddBonus

		item2add.UserCardId = userCardId
		item2add.Who = *userWho
		item2add.IncomeType = balanceAddend.IncomeType
		item2add.BusinessType = balanceAddend.BusinessType
		item2add.PayChan = balanceAddend.PayChan
		item2add.Description = balanceAddend.Description
		isSuccess := t.tbMemberCardAccountLog.AddRecodeWithsession(s.GetSession(), item2add)
		if isSuccess != true {
			err := errors.New("tbMemberCardAccountLog.AddRecodeWithsession")
			s.RollbackWithLogErr(err)
			return err
		}
	}

	s.Commit()
	return err
}



func (t *MchUserMemberCardService) AddBalance2_depression(userCardId int64, addend int) error {
	var maxRetry = 10
	for maxRetry < 10 {
		maxRetry = maxRetry - 1
		isSuccess, err := func ()  (bool, error) {
			s := lego.NewDBSession()
			s.Begin()
			defer s.Close()

			userCard := t.tbMchUserMemberCard.RecodeByIdWithsession(s.GetSession(), userCardId)
			if userCard == nil {
				return false, fmt.Errorf("not found userCardId<%d>", userCardId)
			}


			userCard.Balance = userCard.Balance + addend
			count, err := t.tbMchUserMemberCard.UpdateRecodeWithSession(s.GetSession(), userCard)
			if err != nil {
				s.Rollback()
				return false, err
			}

			if  count <= 0 {
				s.Rollback()
				return false, nil
			}


			s.Commit()
			return true, nil
		}()

		if isSuccess == true {
			return err
		}

	}

	return errors.New("AddBalance failed")
}

func GenerateUserCardCode() int64 {
	id := lego.UUID()
	//idString := utils.Int64ToString(id)
	return id
}

func (t *MchUserMemberCardService) ListMchUserMemberCardByTenant(tid int64, keyword string, orderfiled, order string, page, psize int) ([]MchUserMemberCard, int64, error) {
	return t.tbMchUserMemberCard.ListMchUserMemberCardByTenant(tid, keyword, orderfiled, order, page, psize)
}

func (t *MchUserMemberCardService) UpdateMemberInfo(who *common.Who, userCardId int64, memberInfo *MemberInfo) (error) {

	userCard := t.tbMchUserMemberCard.RecodeById(userCardId)
	if userCard == nil {
		return fmt.Errorf("没有找到用户卡Id<%d>", userCardId)
	}
	rc := new(MchUserMemberCard)
	rc.Id = userCardId
	rc.MemberInfo = *memberInfo

	cols := []string{Feild_MchUserMemberCard.UserName,
		             Feild_MchUserMemberCard.Gender,
		             Feild_MchUserMemberCard.Birthday,
		             Feild_MchUserMemberCard.Phone,
		             Feild_MchUserMemberCard.MemberLabel,
		             Feild_MchUserMemberCard.WechatId,
		             Feild_MchUserMemberCard.Comment,
	}

	_, err := t.tbMchUserMemberCard.UpdateRecode(rc, cols...)
	return err
}

/*
乐观锁例子2（方案二弃用）
	var maxRetry2 = 10
	for maxRetry2 < 10 { //防止死循环
		maxRetry2 = maxRetry2 - 1
		userCard := t.tbMchUserMemberCard.RecodeById(userCardId)
		if userCard == nil {
			return fmt.Errorf("not found userCardId<%d>", userCardId)
		}


		userCard.Balance = userCard.Balance + addend
		err := t.tbMchUserMemberCard.UpdateRecode2(&userCardCopy)
		if err == nil {
			break
		} else {
			if err == ErrDbAffectedRowsIsZero {
				continue
			} else {
				return err
			}
		}
	}
 */